package com.xiebishe.mylibrary.net.retrofit.rxjavaCallAdapter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Function;

/**
 * @class RetryWhenHandler
 * @describe 根据具体错误决定是否要重试[时间退让策略]
 */
public class RetryWhenHandler implements Function<Observable<Throwable>, ObservableSource<Long>> {

    private static final int INITIAL = 1;
    private int maxConnectCount = 1;

    public RetryWhenHandler(int retryCount) {
        this.maxConnectCount += retryCount;
    }

    @Override
    public ObservableSource<Long> apply(Observable<Throwable> throwableObservable) throws Exception {
        return throwableObservable.zipWith(Observable.range(INITIAL, maxConnectCount),
                new BiFunction<Throwable, Integer, ThrowableWrapper>() {
                    @Override
                    public ThrowableWrapper apply(Throwable throwable, Integer i) throws Exception {
                        /**只有网络错误的情况下会重试*/
                        if (throwable instanceof IOException) {
                            return new ThrowableWrapper(throwable, i);
                        }
                        return new ThrowableWrapper(throwable, maxConnectCount);
                    }
                }).concatMap(new Function<ThrowableWrapper, Observable<Long>>() {
            @Override
            public Observable<Long> apply(ThrowableWrapper throwableWrapper) {
                final int retryCount = throwableWrapper.getRetryCount();
                if (maxConnectCount == retryCount) {
                    return Observable.error(throwableWrapper.getSourceThrowable());
                }
                /**时间退让*/
                return Observable.timer((long) Math.pow(2, retryCount), TimeUnit.SECONDS);
            }
        });

    }

    private static final class ThrowableWrapper {

        private Throwable sourceThrowable;
        private Integer retryCount;

        ThrowableWrapper(Throwable sourceThrowable, Integer retryCount) {
            this.sourceThrowable = sourceThrowable;
            this.retryCount = retryCount;
        }

        Throwable getSourceThrowable() {
            return sourceThrowable;
        }

        Integer getRetryCount() {
            return retryCount;
        }
    }

}
